package org.study.lambda;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.*;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class LambdaTest02 {
    public static void main(String[] args) {
        groupingByMapTest();
        groupingByList();
        comparingListTest();
        lambdaSortTest();
    }

    public static void groupingByMapTest() {
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        class Bill implements Serializable {
            private String payFlag;
            private Integer payType;
            private BigDecimal payMoney;
            private LocalDateTime payTime;
        }
        List<Bill> billList = new ArrayList<>() {{
            LocalDateTime now = LocalDateTime.now();

            String uuid01 = "payFlag-001";
            this.add(new Bill(uuid01, 1, BigDecimal.ONE, now));
            this.add(new Bill(uuid01, 1, BigDecimal.TEN, now));
            this.add(new Bill(uuid01, 3, BigDecimal.ZERO, now));
            this.add(new Bill(uuid01, 4, BigDecimal.ONE, now));

            String uuid02 = "payFlag-002";
            this.add(new Bill(uuid02, 1, BigDecimal.ONE, now));
            this.add(new Bill(uuid02, 2, BigDecimal.TEN, now));
            this.add(new Bill(uuid02, 2, BigDecimal.ZERO, now));
            this.add(new Bill(uuid02, 4, BigDecimal.TEN, now));

            String uuid03 = "payFlag-003";
            this.add(new Bill(uuid03, 1, BigDecimal.ONE, now));
            this.add(new Bill(uuid03, 2, BigDecimal.TEN, now));
            this.add(new Bill(uuid03, 3, BigDecimal.ZERO, now));
            this.add(new Bill(uuid03, 3, BigDecimal.ZERO, now));
        }};

        // payFlag 将作为 Map 的 Key
        Map<String, Bill> payFlagMap = billList.stream()
                .collect(Collectors.toMap(Bill::getPayFlag, Function.identity(), (key1, key2) -> key2));
        System.out.println("将 payFlag 作为 Map 的 Key：\n" + payFlagMap);

        Map<String, BigDecimal> decimalMap = billList.parallelStream()
                .filter(item -> StringUtils.isNotBlank(item.getPayFlag()) && item.getPayType() != null && item.getPayMoney() != null)
                .collect(Collectors.toMap(k -> k.getPayFlag() + "-" + k.getPayType(), k -> k.getPayMoney(), (key1, key2) -> key2));
        System.out.println("将 payFlag-payType 作为 Map 的 Key：\n" + decimalMap);


        BigDecimal totalMoney1 = new BigDecimal(billList.stream().mapToDouble(k -> k.getPayMoney().multiply(new BigDecimal(k.getPayType())).doubleValue()).sum());
        System.out.println("金额求和1：\n" + totalMoney1);
        BigDecimal totalMoney2 = billList.stream().map(Bill::getPayMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println("金额求和2：\n" + totalMoney2);
        totalMoney2 = billList.stream().filter(item -> item.getPayType() == 4).map(Bill::getPayMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println("金额求和2：\n" + totalMoney2);
        BigDecimal totalMoney3 = billList.stream().map(i -> {
            if (i.getPayMoney() == null) {
                return BigDecimal.ZERO;
            } else {
                return i.getPayMoney();
            }
        }).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println("金额求和3：\n" + totalMoney3);

        // 嵌套分组：多个属性拼接出⼀个组合属性
        Map<String, List<Bill>> groupingMap01 = billList.stream()
                .collect(Collectors.groupingBy(e -> e.getPayFlag() + "#" + e.getPayType()));
        System.out.println("嵌套分组 - 多个属性拼接出⼀个组合属性：\n" + groupingMap01);

        // 嵌套分组：嵌套调⽤ groupingBy
        Map<String, Map<Integer, List<Bill>>> groupingMap02 = billList.stream()
                .collect(Collectors.groupingBy(Bill::getPayFlag, Collectors.groupingBy(Bill::getPayType)));
        System.out.println("嵌套分组 - 嵌套调⽤ groupingBy：\n" + groupingMap02);

        // 分组求和
        List<Bill> results = groupingMap01.values().stream().map((List<Bill> bills) -> {
            BigDecimal totalPayMoney = BigDecimal.ZERO;
            for (Bill bill : bills) {
                totalPayMoney = totalPayMoney.add(bill.getPayMoney());
            }
            Bill newBill = new Bill(bills.get(0).getPayFlag(), bills.get(0).getPayType(), totalPayMoney, LocalDateTime.now());
            return newBill;
        }).collect(Collectors.toList());
        System.out.println("分组求和：\n" + JSON.toJSONString(results));
    }

    public static void groupingByList() {
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        class User implements Serializable {
            private String name;
            private Integer age;
        }

        List<User> users = new LinkedList<>() {{
            this.add(new User("Jim", 12));
            this.add(new User("John", 18));
            this.add(new User("Tom", 21));
            this.add(new User("Leo", 30));
            this.add(new User("Kate", 44));
            this.add(new User("Lio", 50));
        }};

        Map<String, List<User>> tripleUsers = users.stream()
                .collect(Collectors.groupingBy(user -> {
                    String key;
                    if (user.getAge() <= 20) {
                        key = "less20";
                    } else if (user.getAge() <= 40) {
                        key = "less40";
                    } else {
                        key = "more40";
                    }
                    return key;
                }, Collectors.toList()));
        System.out.println(tripleUsers);
    }

    public static void comparingListTest() {
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        class User implements Serializable {
            private String name;
            private String sex;
            private Integer age;
            private String address;
        }

        List<User> userList = new ArrayList<>() {{
            this.add(new User("小丽", "女", 23, "北京"));
            this.add(new User("张三", "女", 20, "西安"));
            this.add(new User("小王", "男", 2, "广州"));
            this.add(new User("王五", "男", 27, "广州"));
            this.add(new User("小王", "男", 29, "厦门"));
            this.add(new User("小丽", "女", 23, "北京"));
        }};
        System.out.println(userList);
        ArrayList<User> collect = userList.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(user -> user.getName() + ":" + user.getAddress() + ":" + user.getSex()))), ArrayList::new));
        System.out.println(collect);
    }

    public static void lambdaSortTest() {
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        class Message implements Serializable {
            private static final long serialVersionUID = 6989385191570513894L;
            private Long id;
            private String msg;
            @JSONField(format = "yyyy-MM-dd HH:mm:ss")
            private Date sendDate;
        }

        List<Message> msgList = new ArrayList<>();
        msgList.add(new Message(1L, "A", DateUtil.parse("2022-05-10 10:30:10")));
        msgList.add(new Message(4L, "D", DateUtil.parse("2022-05-10 10:30:40")));
        msgList.add(new Message(3L, "C", DateUtil.parse("2022-05-10 10:30:30")));
        msgList.add(new Message(2L, "B", DateUtil.parse("2022-05-10 10:30:20")));

        // 发送时间正序排序
        // msgList.sort((Message m1, Message m2) -> m1.getSendDate().compareTo(m2.getSendDate()));
        msgList.sort(Comparator.comparing(Message::getSendDate));
        // 发送时间倒序排序，发送时间相同则按消息内容字典顺序排序
        // msgList.stream().sorted(Comparator.comparing(Message::getSendDate).reversed().thenComparing(Message::getMsg));
        System.out.println("\n按消息发送时间正序排序：");
        for (Message message : msgList) {
            System.out.println(JSON.toJSONString(message));
        }

        // 消息 ID 倒序排序
        // msgList.sort((Message m1, Message m2) -> m1.getId().compareTo(m2.getId()));
        msgList.sort(Comparator.comparing(Message::getId).reversed());
        System.out.println("\n按消息 ID 倒序排序：");
        for (Message message : msgList) {
            System.out.println(JSON.toJSONString(message));
        }

        // 消息内容字典倒序排序
        msgList.sort((Message m1, Message m2) -> m2.getMsg().compareTo(m1.getMsg()));
        System.out.println("\n按消息内容字典倒序排序：");
        for (Message message : msgList) {
            System.out.println(JSON.toJSONString(message));
        }

        System.out.println();
    }
}
